﻿using System;
using RayDen.Library.Components.Surface;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;

namespace RayEngine.Spectral.Entities.Brdfs
{
    public class SimpleGlass : BrdfBase
    {
        public float n0, n1, R0;

        public SimpleGlass(float no, float ni)
        {
            n0 = no;
            n1 = ni;
            float nc = no;
            float nt = ni;
            float a = nt - nc;
            float b = nt + nc;
            R0 = a * a / (b * b);
        }
        public override BrdfType Type
        {
            get { return BrdfType.Refractive | BrdfType.Specular;  }
        }

        public override BrdfClass Class
        {
            get { return BrdfClass.Glass; }
        }

        public override void F(ShadePointInfo pt, out IColorType fs, BrdfType types = BrdfType.Diffuse)
        {
            throw new NotImplementedException();
        }

        public override void Sample_f(ShadePointInfo pt, float u0, float u1, float u2, 
                                      out Vector Wi, out float radiancePdf, out float pdf, out BsdfEvent @event)
        {
            Vector rayDir = -pt.IncomingDirection;
            var N = pt.GeoNormal;
            var shadeN = pt.ShadingNormal;
            var wi = new Vector();

            var N1 = N.ToVec();
            var reflDir = rayDir - (2f * (Normal.Dot(ref N, ref rayDir))) * N1;

            // Ray from outside going in ?
            bool into = ((Normal.Dot(ref N, ref shadeN)) > 0);

            float nc = n0;
            float nt = n1;
            float nnt = into ? (nc / nt) : (nt / nc);
            float ddn = (rayDir & shadeN.ToVec());
            float cos2t = 1f - nnt * nnt * (1f - ddn * ddn);
            radiancePdf = 0f;
            // Total internal reflection
            if (cos2t < 0f)
            {
                wi = reflDir;
                pdf = 1f;
                @event = BsdfEvent.Reflect | BsdfEvent.Specular;
                radiancePdf = 1f;
                Wi = wi;
                return;
            }

            float kk = (into ? 1f : -1f) * (ddn * nnt + MathLab.Sqrt((cos2t)));
            Vector nkk = kk * N1;
            Vector transDir = (nnt * rayDir - nkk).Normalize();

            float c = 1f - (into ? -ddn : (transDir & N1));

            float Re = R0 + (1f - R0) * c * c * c * c * c;
            float Tr = 1f - Re;
            float P = .25f + .5f * Re;

            if (Tr.NearEqual(0f))
            {
                if (Re.NearEqual(0f))
                {
                    pdf = 0f;
                    @event = BsdfEvent.Absorb;
                }
                else
                {
                    (wi) = reflDir;
                    pdf = 1f;
                    radiancePdf = 1f;
                    @event = BsdfEvent.Reflect | BsdfEvent.Specular;
                    Wi = wi;
                    return;
                }
            }
            else if (Re.NearEqual(0f))
            {
                (wi) = transDir;
                pdf = 1f;
                @event = BsdfEvent.Transmit | BsdfEvent.Specular;
                radiancePdf = 1.0f;                
            }
            else if (u0 < P)
            {

                (wi) = reflDir;
                pdf = P / Re;
                radiancePdf = 1f;
                @event = BsdfEvent.Reflect | BsdfEvent.Specular;
            }
            else
            {

                (wi) = transDir;
                pdf = (1f - P) / Tr;
                @event = BsdfEvent.Transmit | BsdfEvent.Specular;
                radiancePdf = 1.0f;
            }
            Wi = wi;
        }

        public override float Pdf(ref RayDen.Library.Core.Primitives.Vector wo, ref RayDen.Library.Core.Primitives.Vector wi)
        {
            throw new NotImplementedException();
        }
    }
}
